package org.subatu.orm.query.function;

import org.subatu.orm.query.table.QueryColumn;
import org.subatu.orm.util.ClassUtil;
import org.subatu.orm.util.SqlUtil;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class FunctionToQueryColumnUtil {
    private static final Map<Class<?>, QueryColumn> CLASS_FIELD_CACHE = new ConcurrentHashMap<>();

    public static QueryColumn getQueryColumn(Serializable fn) {
        QueryColumn queryColumn = CLASS_FIELD_CACHE.get(fn.getClass());
        if (queryColumn != null) return queryColumn;

        try {
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda lambda = (SerializedLambda) method.invoke(fn);
            String implClass = lambda.getImplClass().replace("/", ".");
            String implMethodName = lambda.getImplMethodName();
            String fieldName = getFieldName(implMethodName);

            Class<?> clazz = Class.forName(implClass);
            Field field = ClassUtil.getField(clazz, fieldName);

            String column = SqlUtil.getColumn(field);
            String tableName = SqlUtil.getTableName(clazz);
            queryColumn = new QueryColumn(column, tableName);
            // 设置 到缓存
            CLASS_FIELD_CACHE.put(fn.getClass(), queryColumn);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | ClassNotFoundException | RuntimeException e) {
            throw new RuntimeException("找不到该字段", e);
        }
        return queryColumn;
    }

    // 根据函数名 获取 字段名
    private static String getFieldName(String implMethodName) {
        String fieldName = toLowerCaseFirstChar(implMethodName.substring(3));
        return fieldName;
    }

    // 首字母小写
    public static String toLowerCaseFirstChar(String word) {
        if (word == null || word.isEmpty()) {
            return word;
        }
        return Character.toLowerCase(word.charAt(0)) + word.substring(1);
    }


    public static <E> List<QueryColumn> toQueryColumn(SFunction<E>... selectFunctions) {
        List<QueryColumn> list = new ArrayList<>();
        for (SFunction<E> function : selectFunctions) {
            QueryColumn queryColumn = getQueryColumn(function);
            list.add(queryColumn);
        }
        return list;
    }
}
